package msrc

import (
	"context"
	"sync"
	"testing"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"gitlab.systems-fd.com/packages/golang/helpers/h"
)

func Test_multiServiceServer_Run(t *testing.T) {
	type fields struct {
		services         map[string]ServiceInterface
		servicesComplete map[string]bool
		serviceStopFunc  *h.SyncValueStruct[context.CancelFunc]
		servicesIsWorkMx *sync.Mutex
		logger           *logrus.Entry
	}
	tests := []struct {
		name        string
		fields      fields
		wantErr     bool
		isRunCalled bool
	}{
		{
			name: "Тестирование отсутствия ошибки, если сервис ее не возвращает",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
			},
			wantErr:     false,
			isRunCalled: true,
		},
		{
			name: "Тестирование наличия ошибки, если сервис ее возвращает",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        errors.New("Test error"),
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
			},
			wantErr:     true,
			isRunCalled: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := &multiServiceServer{
				services:             tt.fields.services,
				servicesComplete:     tt.fields.servicesComplete,
				serviceStopFunc:      tt.fields.serviceStopFunc,
				servicesIsWorkMx:     tt.fields.servicesIsWorkMx,
				isServicesRun:        h.SyncValue[bool](false),
				isShutdownInProgress: h.SyncValue[bool](false),
				logger:               tt.fields.logger,
			}
			if err := m.Run(); (err != nil) != tt.wantErr {
				t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
			}

			for _, s := range tt.fields.services {
				if service, ok := s.(*serviceMock); ok {
					if service.RunIsCalled != tt.isRunCalled {
						t.Errorf("Run() isRunCalled = %v, want %v", service.RunIsCalled, tt.isRunCalled)
					}
				}
			}
		})
	}
}

func Test_multiServiceServer_IsServicesRun(t *testing.T) {
	type fields struct {
		services         map[string]ServiceInterface
		servicesComplete map[string]bool
		serviceStopFunc  *h.SyncValueStruct[context.CancelFunc]
		servicesIsWorkMx *sync.Mutex
		logger           *logrus.Entry
	}
	tests := []struct {
		name   string
		fields fields
	}{
		{
			name: "Тестирование правильности возвращения результата проверки статуса запуска",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-3": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := &multiServiceServer{
				services:             tt.fields.services,
				servicesComplete:     tt.fields.servicesComplete,
				serviceStopFunc:      tt.fields.serviceStopFunc,
				servicesIsWorkMx:     tt.fields.servicesIsWorkMx,
				isServicesRun:        h.SyncValue[bool](false),
				isShutdownInProgress: h.SyncValue[bool](false),
				logger:               tt.fields.logger,
			}

			testServices := func(flagStatus bool) {
				if flagStatus != m.IsServicesRun() {
					t.Errorf("IsServicesRun() flag = %v, want %v", m.IsServicesRun(), flagStatus)
				}
			}

			testServices(false)

			_ = m.Run()
			go func() {
				testServices(true)
			}()

			time.Sleep(300 * time.Millisecond)

			testServices(true)
		})
	}
}

func Test_multiServiceServer_IsServicesAlive(t *testing.T) {
	type fields struct {
		services         map[string]ServiceInterface
		servicesComplete map[string]bool
		serviceStopFunc  *h.SyncValueStruct[context.CancelFunc]
		servicesIsWorkMx *sync.Mutex
		logger           *logrus.Entry
	}
	tests := []struct {
		name   string
		fields fields
	}{
		{
			name: "Тестирование правильности возвращения результата проверки статуса рабочести",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-3": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := &multiServiceServer{
				services:             tt.fields.services,
				servicesComplete:     tt.fields.servicesComplete,
				serviceStopFunc:      tt.fields.serviceStopFunc,
				servicesIsWorkMx:     tt.fields.servicesIsWorkMx,
				isServicesRun:        h.SyncValue[bool](false),
				isShutdownInProgress: h.SyncValue[bool](false),
				logger:               tt.fields.logger,
			}

			testServices := func(flagStatus bool) {
				if flagStatus != m.IsServicesAlive() {
					t.Errorf("IsServicesAlive() flag = %v, want %v", m.IsServicesAlive(), flagStatus)
				}
			}

			testServices(false)

			go func() {
				time.Sleep(100 * time.Millisecond)

				testServices(true)
			}()
			_ = m.Run()

			time.Sleep(300 * time.Millisecond)

			testServices(false)
		})
	}
}

func Test_multiServiceServer_Stop(t *testing.T) {
	type fields struct {
		services         map[string]ServiceInterface
		servicesComplete map[string]bool
		serviceStopFunc  *h.SyncValueStruct[context.CancelFunc]
		servicesIsWorkMx *sync.Mutex
		logger           *logrus.Entry
		wait             int
	}
	tests := []struct {
		name             string
		fields           fields
		isShutdownCalled bool
	}{
		{
			name: "Тестирование правильности работы остановки сервиса",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-3": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
				wait:             800,
			},
			isShutdownCalled: false,
		},
		{
			name: "Тестирование правильности работы остановки сервиса",
			fields: fields{
				services: map[string]ServiceInterface{
					"test-1": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-2": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
					"test-3": &serviceMock{
						RunIsCalled:      false,
						ShutdownIsCalled: false,
						RunResult:        nil,
						RunStat:          false,
					},
				},
				servicesComplete: map[string]bool{},
				serviceStopFunc:  h.SyncValue[context.CancelFunc](func() {}),
				servicesIsWorkMx: &sync.Mutex{},
				logger:           logrus.WithField("prefix", `test`),
				wait:             100,
			},
			isShutdownCalled: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			m := &multiServiceServer{
				services:             tt.fields.services,
				servicesComplete:     tt.fields.servicesComplete,
				serviceStopFunc:      tt.fields.serviceStopFunc,
				servicesIsWorkMx:     tt.fields.servicesIsWorkMx,
				isServicesRun:        h.SyncValue[bool](false),
				isShutdownInProgress: h.SyncValue[bool](false),
				logger:               tt.fields.logger,
			}

			go func() {
				time.Sleep(time.Duration(tt.fields.wait) * time.Millisecond)

				m.Stop()
			}()

			_ = m.Run()

			for _, s := range tt.fields.services {
				if service, ok := s.(*serviceMock); ok {
					if service.ShutdownIsCalled != tt.isShutdownCalled {
						t.Errorf("Stop() isShutdownCalled = %v, want %v", service.ShutdownIsCalled, tt.isShutdownCalled)
					}
				}
			}
		})
	}
}
